/*
 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"

#include "time.h"
#include "sys/time.h"
#include "rs485.h"
#include "led.h"
#include "slave.h"
#include "spp.h"

extern led_obj_t* led1_obj;
extern led_obj_t* led2_obj;
extern char spp_name[16];

#define SPP_TAG "SPP"
#define SPP_SERVER_NAME "SPP_SERVER"
#define EXAMPLE_DEVICE_NAME spp_name //"BT_PORT"   
#define CONFIG_MAX_DATA_LEN 1024

// SPP配置
static const esp_spp_cfg_t bt_spp_cfg = {
    .mode = ESP_SPP_MODE_CB,
    .enable_l2cap_ertm = true,
    /* Only used for ESP_SPP_MODE_VFS mode */
    .tx_buffer_size = 0, 
};

// SPP安全配置:身份验证、SPP角色
static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_AUTHENTICATE;
static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE;

// SPP连接句柄
static uint32_t spp_handle = 0;

// 数据信息
static QueueHandle_t spp_send_queue = NULL;

typedef struct{
    uint8_t* data;
    uint16_t length;
} spp_send_data_t;

// 0: 透传模式 1: 设置模式
static uint8_t u8sppMode = 0;

/**
 * @brief      打印设备地址
 * @param[in]  bda      设备地址
 * @param[in]  str      字符串缓冲区
 * @param[in]  size     字符串缓冲区大小
 * @return     字符串缓冲区地址
 */
static char *bda2str(uint8_t * bda, char *str, size_t size)
{
    if (bda == NULL || str == NULL || size < 18) 
    {
        return NULL;
    }

    uint8_t *p = bda;
    sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
            p[0], p[1], p[2], p[3], p[4], p[5]);
    return str;
}

/**
 * @brief      SPP事件回调函数
 * @param[in]  event    事件类型
 * @param[in]  param    事件参数
 * @note       该函数为 SPP 事件回调函数，处理 SPP 事件，并打印相关日志
 */
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
    char bda_str[18] = {0};

    switch (event) 
    {
    case ESP_SPP_INIT_EVT:
        if (param->init.status == ESP_SPP_SUCCESS) 
        {
            ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT");
            esp_spp_start_srv(sec_mask, role_slave, 0, SPP_SERVER_NAME);
        } 
        else 
        {
            ESP_LOGE(SPP_TAG, "ESP_SPP_INIT_EVT status:%d", param->init.status);
        }
        break;
    case ESP_SPP_DISCOVERY_COMP_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT");
        break;
    case ESP_SPP_OPEN_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT");
        break;
    case ESP_SPP_CLOSE_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT status:%d handle:%"PRIu32" close_by_remote:%d", 
        param->close.status,param->close.handle, param->close.async);
        break;
    case ESP_SPP_START_EVT:
        if (param->start.status == ESP_SPP_SUCCESS) 
        {
            ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT handle:%"PRIu32" sec_id:%d scn:%d", 
            param->start.handle, param->start.sec_id, param->start.scn);
            esp_bt_gap_set_device_name(EXAMPLE_DEVICE_NAME);
            esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
        } 
        else 
        {
            ESP_LOGE(SPP_TAG, "ESP_SPP_START_EVT status:%d", param->start.status);
        }
        break;
    case ESP_SPP_CL_INIT_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT");
        break;
    case ESP_SPP_DATA_IND_EVT:
        /*
         * We only show the data in which the data length is less than 128 here. If you want to print the data and
         * the data rate is high, it is strongly recommended to process them in other lower priority application task
         * rather than in this callback directly. Since the printing takes too much time, it may stuck the Bluetooth
         * stack and also have a effect on the throughput!
         */
        ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT len:%d handle:%"PRIu32,
                 param->data_ind.len, param->data_ind.handle);
        if (param->data_ind.len <= CONFIG_MAX_DATA_LEN) 
        {
            esp_log_buffer_hex(SPP_TAG, param->data_ind.data, param->data_ind.len);
            if(u8sppMode == 0)
            {
                
                rs485_send_data(param->data_ind.data, param->data_ind.len);
            }
            else
            {
                slave_send_data(param->data_ind.data, param->data_ind.len, spp_send_data);
            }
            ESP_LOGD(SPP_TAG, "Data: %d",u8sppMode);
        }
        break;
    case ESP_SPP_CONG_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_CONG_EVT");
        break;
    case ESP_SPP_WRITE_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT");
        break;
    case ESP_SPP_SRV_OPEN_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT status:%d handle:%"PRIu32", rem_bda:[%s]", 
        param->srv_open.status,param->srv_open.handle, bda2str(param->srv_open.rem_bda, bda_str, sizeof(bda_str)));
        // gettimeofday(&time_old, NULL);
        // 记录连接句柄
        spp_handle = param->srv_open.handle;
        // Led
        led_ctrl(led2_obj, LED_ON);  
        break;
    case ESP_SPP_SRV_STOP_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_STOP_EVT");
        // 断开连接后清空连接句柄
        spp_handle = 0;  
        led_ctrl(led2_obj, LED_OFF);  
        break;
    case ESP_SPP_UNINIT_EVT:
        ESP_LOGI(SPP_TAG, "ESP_SPP_UNINIT_EVT");
        break;
    default:
        break;
    }
}

void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
    char bda_str[18] = {0};

    switch (event) 
    {
        // 验证完成事件
        case ESP_BT_GAP_AUTH_CMPL_EVT:
        {
            if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) 
            {
                ESP_LOGI(SPP_TAG, "authentication success: %s bda:[%s]", param->auth_cmpl.device_name,
                            bda2str(param->auth_cmpl.bda, bda_str, sizeof(bda_str)));
            } 
            else 
            {
                ESP_LOGE(SPP_TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
            }
            break;
        }
        // 申请PIN码事件
        case ESP_BT_GAP_PIN_REQ_EVT:
        {
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
            if (param->pin_req.min_16_digit) 
            {
                ESP_LOGI(SPP_TAG, "Input pin code: 0000 0000 0000 0000");
                esp_bt_pin_code_t pin_code = {0};
                esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
            } 
            else 
            {
                ESP_LOGI(SPP_TAG, "Input pin code: 1234");
                esp_bt_pin_code_t pin_code;
                pin_code[0] = '1';
                pin_code[1] = '2';
                pin_code[2] = '3';
                pin_code[3] = '4';
                esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
            }
            break;
        }
        // 简单配对回复事件
        case ESP_BT_GAP_CFM_REQ_EVT:
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %"PRIu32, param->cfm_req.num_val);
            esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
            break;
        // 安全简单配对密钥通知事件
        case ESP_BT_GAP_KEY_NOTIF_EVT:
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%"PRIu32, param->key_notif.passkey);
            break;
        // 安全简单配对密钥请求事件
        case ESP_BT_GAP_KEY_REQ_EVT:
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
            break;
        // 连接状态改变事件
        case ESP_BT_GAP_MODE_CHG_EVT:
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d bda:[%s]", param->mode_chg.mode,
                        bda2str(param->mode_chg.bda, bda_str, sizeof(bda_str)));
            break;
        // ACL连接完全事件
        case ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT:
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT");
            led_ctrl(led2_obj, LED_BLINK);
            break;
        // ACL 断开连接完成状态事件
        case ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT:
            ESP_LOGI(SPP_TAG, "ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT");
            led_ctrl(led2_obj, LED_OFF);
            break;
        default: 
        {
            ESP_LOGI(SPP_TAG, "gap event: %d", event);
            break;
        }
    }
    return;
}


/**
 * @brief      发送数据
 * @param[in]  data     待发送数据
 * @param[in]  length   待发送数据长度
 * @note       发送数据到 SPP 客户端
 */
void spp_send_data(uint8_t* data, uint16_t length)
{
    if(data && length > 0)
    {
        if(spp_send_queue)
        {
            spp_send_data_t send_data;
            uint8_t* data_copy = (uint8_t*)pvPortMalloc(length);
            if(data_copy)
            {
                memcpy(data_copy, data, length);
                send_data.data = data_copy;
                send_data.length = length;
                if(xQueueSend(spp_send_queue, &send_data, 0) != pdTRUE)
                {
                    ESP_LOGE(SPP_TAG, "Failed to send data, send queue is full");
                    vPortFree(data_copy);
                }
            }
            else
            {
                ESP_LOGE(SPP_TAG, "Failed to send data, memory allocation failed");
            }
        }
        else
        {
            ESP_LOGE(SPP_TAG, "Failed to send data, send queue is NULL");
        }
    }
}

/**
 * @brief      设置 SPP 模式
 * @param[in]  mode     模式 0: 透传模式 1: 设置模式
 */
void spp_set_mode(uint8_t mode)
{
    u8sppMode = (mode = 0? 0 : 1);
}

/**
 * @brief      SPP发送数据任务
 * @note       该任务负责从队列中获取数据并发送到 SPP 客户端
 */
void spp_send_data_task(void *pvParameters)
{
    uint8_t* data = (uint8_t*)pvPortMalloc(CONFIG_MAX_DATA_LEN);

    spp_send_queue = xQueueCreate(4, sizeof(spp_send_data_t));

    spp_send_data_t send_data;

    if(!spp_send_queue || !data)
    {
        ESP_LOGE(SPP_TAG, "Failed to create send queue");
        return;
    }
    while (1) 
    {
        // 等待接收数据
        if(xQueueReceive(spp_send_queue, &send_data, portMAX_DELAY) == pdTRUE)
        {
            if ((spp_handle != 0) && (send_data.length > 0) && (send_data.data != NULL))
            {
                memcpy(data, send_data.data, send_data.length);
                esp_err_t ret = esp_spp_write(spp_handle, send_data.length, data);
                vPortFree(send_data.data);

                if (ret == ESP_OK) 
                {
                    ESP_LOGI(SPP_TAG, "Data sent: %s", data);
                } 
                else 
                {
                    ESP_LOGE(SPP_TAG, "Failed to send data: %s", esp_err_to_name(ret));
                }
            } 
            else 
            {
                ESP_LOGE(SPP_TAG, "No client connected");
            }
        }
        else
        {
            break;
        }
    }
    vPortFree(data);
    data = NULL;
    vQueueDelete(spp_send_queue);
    spp_send_queue = NULL;
}

void spp_app_main(void)
{
    char bda_str[18] = {0};

    esp_err_t ret;

    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();

    if ((ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg)) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bluedroid_enable()) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s enable bluedroid failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s gap register failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_spp_register_callback(esp_spp_cb)) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s spp register failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_spp_enhanced_init(&bt_spp_cfg)) != ESP_OK) 
    {
        ESP_LOGE(SPP_TAG, "%s spp init failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    /* Set default parameters for Secure Simple Pairing */
    esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
    esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
    esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));

    /*
     * Set default parameters for Legacy Pairing
     * Use variable pin, input pin code when pairing
     */
    esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
    esp_bt_pin_code_t pin_code;
    esp_bt_gap_set_pin(pin_type, 0, pin_code);

    ESP_LOGI(SPP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str)));

    xTaskCreate(spp_send_data_task, "spp_send_data_task", 2048, NULL, 10, NULL);
}
